package com.dxp.utils;

import com.apache.tools.StrUtil;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.IOException;
import java.io.StringReader;
import java.util.*;

/**
 * @description: 通过XSD文件对XML文件进行格式验证
 * @author: Hou Dayu
 * @date: Created in 2020/2/23
 */
public class XMLUtil {
    private static Logger log = Logger.getLogger(XMLUtil.class);

    public static String validateXml(String xsdStr, String xmlStr)
            throws SAXException, IOException {
        try {
            // 建立schema工厂
            SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
            // 利用schema工厂，接收验证文档文件对象生成Schema对象
            xsdStr = "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" >\n" + xsdStr+"\n</xs:schema>";
            StreamSource schemaSource = new StreamSource(IOUtils.toInputStream(xsdStr, "UTF-8"));
            Schema schema = schemaFactory.newSchema(schemaSource);
            // 通过Schema产生针对于此Schema的验证器，利用schenaFile进行验证
            Validator validator = schema.newValidator();
            // 得到验证的数据源
            Source source = new StreamSource(IOUtils.toInputStream(xmlStr, "UTF-8"));
            // 开始验证，成功输出success!!!，失败输出fail
            validator.validate(source);
        }catch (SAXException e){
            return e.getMessage();
        }catch (IOException e){
            return e.getMessage();
        }
        return "";
    }

    /**
     * XML格式字符串转换成map,包括List
     * @param xml
     */
    public static Map resolveXmlToMapList(String xml) throws Exception{
        try {
            Map reMap = new HashMap();
             Document document = DocumentHelper.parseText(xml);
             Element nodesElement = document.getRootElement();
            List nodes = nodesElement.elements();
            for (Iterator its = nodes.iterator(); its.hasNext(); ) {
                 Element nodeElement = ( Element) its.next();
                if (nodeElement.isTextOnly()) {
                    reMap.put(nodeElement.getName(), nodeElement.getText());
                } else {
                    if(nodeElement.elements().size()==1){
                        Map map = xmlToMap(nodeElement);
                        reMap.put(nodeElement.getName(), map);
                    }else {
                        List<Map> reList = xmlToList(nodeElement);
                        reMap.put(nodeElement.getName(), reList);
                    }
                }
            }
            reMap.put(nodesElement.getName(),nodesElement.getName());
            nodes = null;
            nodesElement = null;
            document = null;
            return reMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /***
     * XML格式字符串转换成list
     * @return
     */
    private static List xmlToList(Element nodesElement) {
        try {
            List<Map> list = new ArrayList<Map>();
            List nodes = nodesElement.elements();
            for (Iterator its = nodes.iterator(); its.hasNext();) {
                 Element nodeElement = ( Element) its.next();
                Map map = xmlToMap(nodeElement);
                list.add(map);
            }
            nodes = null;
            nodesElement = null;
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /***
     *XML格式字符串转换成MAP,不包括List
     * @return
     */
    private static Map xmlToMap(Element nodeElement) {
        try {
            Map map = new HashMap();
            List node = nodeElement.elements();
            if (node.size() == 0) {
                map.put(nodeElement.getName(), nodeElement.getTextTrim());
            } else {
                for (Iterator it = node.iterator(); it.hasNext(); ) {
                    Element elm = (Element) it.next();
                    if (nodeElement.isTextOnly()) {
                        map.put(elm.getName(), elm.getText());
                    } else {
                        int count = elm.elements().size();
                        if(count ==1){
                            Map map2 = xmlToMap(elm);
                            map.put(elm.getName(), map2);
                        } else if(count > 1){
                            List<Map> reList = xmlToList(elm);
                            map.put(elm.getName(), reList);
                        }else{
                            map.put(elm.getName(), elm.getTextTrim());
                        }
                    }
                }
            }
            node = null;
            nodeElement = null;
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String callMapToXML(Map map) {
        StringBuffer sb = new StringBuffer();
        String rootName = String.valueOf(map.get("rootName"));
        sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        if(StrUtil.isNotNull(rootName)){
            map.remove("rootName");
            sb.append("<"+rootName+">");
        }
        mapToXMLTest2(map, sb);
        if(StrUtil.isNotNull(rootName)){
            sb.append("</"+rootName+">");
        }
        log.info("将Map转成Xml{}" +sb.toString());
        try {
            return sb.toString();
        } catch (Exception e) {
            log.error(e);
        }
        return null;
    }

    private static void mapToXMLTest2(Map map, StringBuffer sb) {
        Set set = map.keySet();
        for (Iterator it = set.iterator(); it.hasNext();) {
            String key = (String) it.next();
            Object value = map.get(key);
            if (null == value)
                value = "";
            if (value.getClass().getName().equals("java.util.ArrayList")) {
                ArrayList list = (ArrayList) map.get(key);
                //sb.append("<" + key + ">");
                for (int i = 0; i < list.size(); i++) {
                    HashMap hm = (HashMap) list.get(i);
                    mapToXMLTest2(hm, sb);
                }
                //sb.append("</" + key + ">");
            } else {
                if (value instanceof HashMap) {
                    sb.append("<" + key + ">");
                    mapToXMLTest2((HashMap) value, sb);
                    sb.append("</" + key + ">");
                } else {
                    sb.append("<" + key + ">" + value + "</" + key + ">");
                }
            }
        }
    }

    public static boolean isXmlDocument(String rtnMsg){
        boolean flag = true;
        try {
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
            builder.parse(new InputSource(new StringReader(rtnMsg)));
        } catch (Exception e) {
            flag = false;
        }
        return flag;
    }

    public static void main(String[] args) {
        String xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><birthdate>\n" +
                "<user>\n" +
                "<month>January</month>\n" +
                "<day>21</day>\n" +
                "<year>1983</year>\n" +
                "<name>aaa</name>\n" +
                "</user>\n" +
                "</birthdate>";
        String xsdStr = "<xs:element name=\"birthdate\">\n" +
                "<xs:complexType>\n" +
                "<xs:sequence>\n" +
                "<xs:element name=\"user\">\n" +
                "<xs:complexType>\n" +
                "<xs:sequence>\n" +
                "<xs:element name=\"month\" type=\"xs:string\" />\n" +
                "<xs:element name=\"day\" type=\"xs:int\" />\n" +
                "<xs:element name=\"year\" type=\"xs:int\" />\n" +
                "<xs:element name=\"name\" type=\"xs:string\" />\n" +
                "</xs:sequence>\n" +
                "</xs:complexType>\n" +
                "</xs:element>\n" +
                "</xs:sequence>\n" +
                "</xs:complexType>\n" +
                "</xs:element>";
        try {
            //System.out.println(XMLUtil.validateXml(xsdStr, xmlStr));
            String xmlStr2 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                    "<root>\n" +
                    "<sourceCode>test</sourceCode>\n" +
                    "<methodKey>sendmsgss</methodKey>\n" +
                    "<timestamp>242323221112</timestamp>\n" +
                    "<data>\n" +
                    "<msg>swewewesdsdsdeewewww</msg>\n" +
                    "<ddd>测试Kafka</ddd>\n" +
                    "</data>\n" +
                    "</root>";
            Map map = resolveXmlToMapList(xmlStr2);
            Object obj = map.get("data");
            Map xml = null;
            if(obj instanceof List){
                xml = new HashMap();
                xml.put(map.get("rootName"),obj);
            }else{
                xml = (Map)obj;
            }
            System.out.println(callMapToXML(xml));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
